<template>
    <div class="viewport" ref="viewport" @scroll="scrollFn">
        <div class="scroll-bar" ref="scrollBar">
            <!-- 自己做个滚动条 -->
        </div>
         <div class="scroll-list" :style="{transform:`translate3d(0,${offset}px,0 )`}">
            <!-- 真实渲染的内容 -->
            <div v-for="item in visibleData" :key="item.id" :vid="item.id" ref="items">
                <slot :item="item"></slot>
            </div>
        </div>
    </div>
</template>
<script>
import throttle from 'lodash/throttle';
export default {
    props: {
      size: Number, //每项列表高度
      remain: Number, //可见多少个
      items: Array,
      variable: Boolean //高度是否固定 true为不知道高度多高
    },
    data(){
      return {
          start: 0,
          end: this.remain, //默认显示8个
          offset:0,
          positions: []
      }
    },
    computed: {
      //渲染3屏
      preCount(){//前面预留几个
        return Math.min(this.start,this.remain);
      },
      nextCount(){//后面预留几个
        return Math.min(this.items.length - this.end,this.remain);
      },
      //可见的数据
      visibleData(){
          let start = this.start - this.preCount;
          let end = this.end + this.nextCount;
          return this.items.slice(start,end);
      }
  },
  created(){
      this.scrollFn = throttle(this.handleScroll, 200, {leading: false});
  },
  mounted(){
      this.$refs.viewport.style.height = this.size * this.remain + 'px';
      this.$refs.scrollBar.style.height = this.size * this.items.length + 'px';

      //加载完毕 需要缓存每一项的高度
      this.cacheList();//先记录好，等会滚动的时候，去渲染页面时获取真实dom高度来更新缓存
      //再重新计算滚动条的高度

  },
  updated(){
      //页面渲染完成后需要根据当前展示的数据更新缓存区的内容
      this.$nextTick(()=>{
            let nodes = this.$refs.items;
            if(!(nodes && nodes.length >0)){
                return;
            }
            nodes.forEach(node=>{
                let {height} = node.getBoundingClientRect();
                let id = node.getAttribute('vid') - 0;
                let oldHeight = this.positions[id].height;
                let val = oldHeight - height;
                if(val){ //如果缓存结果和其真实的高度一样，就不需要继续操作
                    this.positions[id].height = height;
                    this.positions[id].bottom = this.positions[id].bottom - val;//底部增加了

                    for(let i = id+1;i<this.positions.length;i++){
                        this.positions[i].top = this.positions[i-1].bottom;
                        this.positions[i].bottom = this.positions[i].bottom - val;
                    }
                }
                //只要更新过，算出滚动条的最新高度 动态计算滚动条的高度
                this.$refs.scrollBar.style.height = this.positions[this.positions.length-1].bottom + 'px';
            })
      })
  },
  methods:{
      getStartIndex(value){
        let start = 0,end = this.positions.length -1;
        let temp = null;
        while(start<=end){
            let mid = (start+end)>>1;
            let midVal = this.positions[mid].bottom;
            if(midVal === value){
                return mid + 1;
            }else if(midVal < value){
                start = mid + 1;
            }else if(midVal > value){
                if(temp == null || temp > mid){
                    temp = mid;//找到范围
                }
                end = mid - 1;
            }
        }
        return temp;
      },
      handleScroll(){
          //应该先算出来当前滚过去几个了，当前应该从第几个开始
          let scrollTop = this.$refs.viewport.scrollTop;
          console.log('scroll',scrollTop)
          if(this.variable){
              //使用二分查找找到对应的记录
              this.start = this.getStartIndex(scrollTop);
              this.end = this.start + this.remain;
              this.offset = this.positions[this.start - this.preCount] ?
              this.positions[this.start - this.preCount].top : 0;
          }else{
            //获取当前应该从第几个开始渲染
            this.start = Math.floor(scrollTop/this.size);
            this.end = this.start + this.remain;
            //定位当前可视区域
            this.offset = this.start * this.size - this.size * this.preCount;
          }
      },
      cacheList(){
         this.positions =  this.items.map((item,index)=>({
              height: this.size,
              top: index * this.size,
              bottom: (index+1) * this.size
          }));
          console.log(this.positions,'positions')
      }
  }
}
</script>
<style lang="less">
.viewport {
    overflow-y: scroll;
    position: relative;
    .scroll-list {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
    }
}
</style>
